﻿using BlogDemo.Common;
using BlogDemo.Data;
using BlogDemo.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace BlogDemo.Infrastructure
{

    public class EfRepository<T> : IRepository<T> where T : class, IEntity
    {
        private readonly IDbContext _context;

        protected IDbContext Context { get { return _context; } }

        private DbSet<T> _dbSet;

        /// <summary>
        /// Gets a table
        /// </summary>
        public virtual IQueryable<T> Table
        {
            get
            {
                return this.DbSet;
            }
        }
        /// <summary>
        /// Gets a table with "no tracking" enabled (EF feature) Use it only when you load record(s) only for read-only operations
        /// </summary>
        public virtual IQueryable<T> TableNoTracking
        {
            get
            {
                return this.DbSet.AsNoTracking();
            }
        }

        /// <summary>
        /// Entities
        /// </summary>
        protected virtual DbSet<T> DbSet
        {
            get
            {
                if (_dbSet == null)
                    _dbSet = _context.Set<T>();
                return _dbSet;
            }
        }
        public EfRepository(IDbContext dbContext)
        {
            this._context = dbContext;
            this._dbSet = _context.Set<T>();
        }

        public T Find(params object[] Id)
        {
            return _dbSet.Find(Id);
        }

        public T Find(Expression<Func<T, bool>> predicate)
        {
            return Table.FirstOrDefault(predicate);
        }
        public long Count(Expression<Func<T, bool>> predicate)
        {
            return TableNoTracking.LongCount(predicate);
        }

        public bool Any(Expression<Func<T, bool>> predicate)
        {
            return TableNoTracking.Any(predicate);
        }
        /// <summary>
        /// 查找实体列表
        /// </summary>
        /// <param name="predicate">查询条件谓语表达式</param>
        /// <returns></returns>
        public IEnumerable<T> Query(Expression<Func<T, bool>> predicate = null)
        {
            return TableNoTracking.Where(predicate);
        }
        /// <summary>
        /// 查找符合特定表达式<see cref="TResult"/>列表
        /// </summary>
        /// <typeparam name="TResult">返回类型</typeparam>
        /// <param name="predicate">查询条件谓语表达式</param>
        /// <param name="selector">筛选的字段表达式</param>
        /// <returns></returns>
        public IEnumerable<TResult> Query<TResult>(Expression<Func<T, bool>> predicate, Expression<Func<T, TResult>> selector)
        {
            return TableNoTracking.Where(predicate).Select(selector);
        }

        public void Add(T entity)
        {
            _dbSet.Add(entity);
        }

        public void Update(T entity)
        {
            _context.Entry<T>(entity).State = EntityState.Modified;
        }

        public void Remove(T entity)
        {
            _dbSet.Attach(entity);
            _dbSet.Remove(entity);
        }

        internal IQueryable<T> OrderBy(IQueryable<T> source, string orderByProperty, bool desc)
        {
            string command = desc ? "OrderByDescending" : "OrderBy";

            var type = typeof(T);
            var property = type.GetProperty(orderByProperty);
            var parameter = Expression.Parameter(type, "p");
            var propertyAccess = Expression.MakeMemberAccess(parameter, property);
            var orderByExpression = Expression.Lambda(propertyAccess, parameter);
            var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExpression));

            return source.Provider.CreateQuery<T>(resultExpression);
        }
        #region Async

        /// <summary>
        /// 获取异步计数
        /// </summary>
        /// <param name="predicate">查询条件谓语表达式</param>
        /// <returns>计数</returns>
        public async Task<long> CountAsync(Expression<Func<T, bool>> predicate = null)
        {
            return await TableNoTracking.LongCountAsync(predicate);
        }
        /// <summary>
        /// 获取指定表达式的是否存在
        /// </summary>
        /// <param name="predicate">查询条件谓语表达式</param>
        /// <returns></returns>
        public async Task<bool> AnyAsync(Expression<Func<T, bool>> predicate = null)
        {
            return await TableNoTracking.AnyAsync(predicate);
        }
        /// <summary>
        /// 获取指定标识的实体
        /// </summary>
        public async Task<T> FindAsync(params object[] Id)
        {
            return await _dbSet.FindAsync(Id);
        }
        /// <summary>
        /// 获取指定标识的实体
        /// </summary>
        ///<param name="predicate">查询条件谓语表达式</param>
        public async Task<T> FindAsync(Expression<Func<T, bool>> predicate)
        {
            return await Table.FirstOrDefaultAsync(predicate);
        }
        /// <summary>
        /// 查找符合特定表达式实体列表
        /// </summary>
        /// <param name="predicate">查询条件谓语表达式</param>
        /// <returns></returns>
        public async Task<IEnumerable<T>> QueryAsync(Expression<Func<T, bool>> predicate = null)
        {
            return await TableNoTracking
                .Where(predicate)
                .ToListAsync();
        }
        /// <summary>
        /// 查找符合特定表达式<see cref="TResult"/>列表
        /// </summary>
        /// <typeparam name="TResult">返回类型</typeparam>
        /// <param name="predicate">查询条件谓语表达式</param>
        /// <param name="selector">筛选的字段表达式</param>
        /// <returns></returns>
        public async Task<IEnumerable<TResult>> QueryAsync<TResult>(Expression<Func<T, bool>> predicate, Expression<Func<T, TResult>> selector)
        {
            return await TableNoTracking
                .Where(predicate)
                .Select(selector)
                .ToListAsync();
        }
        // <summary>
        /// 添加实体.
        /// </summary>
        public async Task AddAsync(T entity)
        {
            await _dbSet.AddAsync(entity);
        }
        // <summary>
        /// 修改实体.
        /// </summary>
        public async Task UpdateAsync(T entity)
        {
            _context.Entry<T>(entity).State = EntityState.Modified;
            await Task.CompletedTask;
        }
        // <summary>
        /// 移除实体.
        /// </summary>
        public async Task RemoveAsync(T entity)
        {
            _dbSet.Remove(entity);
            await Task.CompletedTask;
        }
        #endregion
        #region IDisposable

        public void Dispose()
        {
            if (_context != null)
                _context.Dispose();
        }

        #endregion
    }

    /// <summary>
    ///     EntityFramework仓储操作基类
    /// </summary>
    /// <typeparam name="TEntity">动态实体类型</typeparam>
    public class EfRepository<TEntity, TKey> : EfRepository<TEntity>, IRepository<TEntity, TKey>
        where TEntity : class, IEntity, IEntity<TKey>
        where TKey : IComparable<TKey>
    {
        public EfRepository(IDbContext dbContext) : base(dbContext)
        {

        }

        public TEntity Find(TKey id)
        {
            return base.DbSet.Find(id);
        }

        public int Update(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity>> updateor)
        {
            //Expression<Func<TSource, TResult>> selector

            ///Task<IQueryable<TResult>> QueryAsync<TResult>(Expression<Func<T, bool>> predicate, Expression<Func<T, TResult>> selector);
            ///DbSet
            // return _entities.Where(predicate).Update(updateor);
            return 1;
        }
        public bool Update(TEntity entity, bool isSaveChange = true, List<string> updatePropertyList = null, bool modified = true)
        {
            if (entity == null)
            {
                return false;
            }
            DbSet.Attach(entity);
            var entry = Context.Entry(entity);
            if (updatePropertyList == null)
            {
                entry.State = EntityState.Modified;//全字段更新
            }
            else
            {
                if (modified)
                {
                    updatePropertyList.ForEach(propertyName =>
                    {
                        entry.Property(propertyName).IsModified = true; //部分字段更新的写法
                    });
                }
                else
                {
                    entry.State = EntityState.Modified;//全字段更新
                    updatePropertyList.ForEach(propertyName =>
                    {
                        entry.Property(propertyName).IsModified = false; //部分字段不更新的写法
                    });
                }
            }
            if (isSaveChange)
            {
                return Context.SaveChanges() > 0;
            }
            return false;
        }
    }

}
